package cn.tnar.yunpark.huasai;

import cn.tnar.yunpark.util.Util;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufProcessor;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.ByteOrder;
import java.util.List;

/**
 * Created by tieh on 2016/9/23.
 */
public class HuasaiMessageDecoder extends ByteToMessageDecoder {

    private static final Logger log = LoggerFactory.getLogger(HuasaiSensorMessage.class);

    public static final byte SENSOR_HEADER = 0x20;
    public static final byte HOST_HEADER = 0x21;
    public static final int ACK_HEADER = 0x77;

    private static final int SENSOR_DATA_LEN = 36;
    private static final int HOST_DATA_LEN = 25;

    private static final ByteBufProcessor HEADER_FINDER = new ByteBufProcessor() {
        @Override
        public boolean process(byte b) throws Exception {
            return b == SENSOR_HEADER || b == HOST_HEADER;
        }
    };

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) throws Exception {
        if (buf.readableBytes() <= 0) {
            return;
        }

        log.debug("<= RX " + Util.toHex(buf));

        int headIndex = findHead(buf);
        if (headIndex == -1) {
            // 没找到，全部忽略
            buf.skipBytes(buf.readableBytes());
            log.warn("Head not found");
            return;
        }
        buf.readerIndex(headIndex);
        byte type = buf.getByte(headIndex);

        // CRC校验
        int readable = buf.readableBytes();
        if (type == SENSOR_HEADER) {
            if (readable < SENSOR_DATA_LEN) {
                return;
            }
            if (checkCrc(buf, headIndex, SENSOR_DATA_LEN - 2)) {
                // 收到一个地磁数据包
                log.info("<= RX SDATA " + Util.toHex(buf, SENSOR_DATA_LEN));
                HuasaiSensorMessage msg = HuasaiSensorMessage.parse(buf);
                list.add(msg);

                // 回复ACK
                HuasaiAck ack = new HuasaiAck(msg.getStopSn());
                ByteBuf ackBuf = ack.encode();
                log.info("=> TX SDATA ACK " + Util.toHex(ackBuf));
                ctx.writeAndFlush(ackBuf);
            } else {
                log.warn("CRC16 check error");
                buf.skipBytes(1);
            }
        } else if (type == HOST_HEADER) {
            if (readable < HOST_DATA_LEN) {
                return;
            }
            if (checkCrc(buf, headIndex, HOST_DATA_LEN - 2)) {
                // 收到一个主机状态包
                log.info("<= RX HOST " + Util.toHex(buf, HOST_DATA_LEN));
                HuasaiHostMessage msg = HuasaiHostMessage.parse(buf);

                // 回复ACK
                HuasaiAck ack = new HuasaiAck(0);
                ByteBuf ackBuf = ack.encode();
                log.info("=> TX HOST ACK " + Util.toHex(ackBuf));
                ctx.writeAndFlush(ackBuf);
            } else {
                log.warn("CRC16 check error");
                buf.skipBytes(1);
            }
        }
    }

    private static int findHead(ByteBuf buf) {
        int index = -1;
        for (int i = buf.readerIndex(); i < buf.readerIndex() + buf.readableBytes(); i++) {
            byte b = buf.getByte(i);
            if (b == HOST_HEADER || b == SENSOR_HEADER) {
                index = i;
                break;
            }
        }
        return index;
    }

    /**
     *
     * @param buf
     * @param offset    要校验的数据段开始
     * @param len       要校验的数据段长度，接下来两个字节是CRC
     * @return
     */
    public static boolean checkCrc(ByteBuf buf, int offset, int len) {
        ByteBuf leBuf = buf.order(ByteOrder.LITTLE_ENDIAN);
        int crc = leBuf.getUnsignedShort(offset + len);
        int calcCrc = CRC16.calc(buf, offset, len);
        if (crc != calcCrc) {
            log.warn(String.format("CRC校验错误: rcv=%04x,calc=%04x", crc, calcCrc));
        }
        return crc == calcCrc;
    }
}
